﻿using System;
using System.Collections.Generic;
using System.Data;
using System.IO;
using System.Linq;
using NHibernate;
using NHibernate.Cfg;
using NHibernate.Dialect;
using NHibernate.Engine;
using NHibernate.SqlTypes;
using NHibernate.Tool.hbm2ddl;
using NUnit.Framework;

namespace Pinkslip.Test
{
    /// <summary>
    /// Tests all NHibernate configurations. This test ensures certain quality constraints should be followed in
    /// data layer. And also, this should pass if database configuration is setup correctly in dev machine
    /// <list type="Table">
    ///  <listheader>Quality Checks</listheader>
    ///     <item>Ensures Mapping (hbm files) should be created separately for each entity</item>
    ///     <item>Ensures Mapping is created for atleast one dialect (SQL Server 2005)</item>
    ///     <item>Ensures the session can be opened/closed</item>
    /// </list>
    /// </summary>   
    [TestFixture]
    [Category("Configuration")]
    public class DataObjectConfiguration
    {
        private const string _DefaultPinkslipDataPath = @"../../../Pinkslip.Data";
        private const string _HbmFilePattern = "*.hbm.xml";
        private Configuration conf;
        private Mappings map;

        [SetUp]
        public void TestSetup()
        {
            try
            {
                conf = new Configuration().Configure();
                map = conf.CreateMappings(new MsSql2005Dialect());
            }
            catch (Exception e)
            {
                Assert.Fail("Configuration initialization failed [{0}]", e.Message);
            }
        }

        [TestFixtureTearDown]
        public void TestTearDown()
        {
            conf = null;
            map = null;
        }

        [Test]
        public void AreMappingConfigured()
        {
            Assert.IsTrue(conf.ClassMappings != null, "Class mappings are not generated");
            Assert.IsTrue(map != null, "Mapping is null");
        }

        [Test]
        public void HasSeparateConfigFiles()
        {
            Func<String, int> CountHbmFilesInDirectory =
                dx => Directory.GetFiles(dx, _HbmFilePattern, SearchOption.TopDirectoryOnly).Length;

            Assert.AreEqual(CountHbmFilesInDirectory(_DefaultPinkslipDataPath), conf.ClassMappings.Count,
                            "Each persistence class should be defined in separate hibernate configuration file");
        }

        [Test]
        public void HasAnyCheckConstraints()
        {
            bool anyHasConstraints = map.IterateTables.All(
                t => t.ColumnIterator.Any(
                         x => x.HasCheckConstraint));

            Assert.IsFalse(anyHasConstraints, "Check constraints in table level is not allowed");
        }

        [Test]
        public void AreStringDataTypeMissingMaxLength()
        {
            //TODO: This test is not implemented yet. NHibernate does not seems to be 
            //TODO: generating column length while configuration is setup.

            /*
            IMapping m = conf.BuildMapping();
            

            foreach (var table in map.IterateTables)
                foreach (var column in table.ColumnIterator)
                    if (column.GetSqlType(new MsSql2005Dialect(), m).Contains("char"))
                    {
                        if (!table.PrimaryKey.Name.Equals(column.Name))
                            Assert.IsTrue(column.IsLengthDefined(),
                                          "String data type [{0}] is missing its MaxLength", column.Name);
                    }
             */

        }

        [Test]
        public void IsSchemaCreationWorks()
        {
            try
            {
                String[] schema = conf.GenerateSchemaCreationScript(new MsSql2005Dialect());
                Assert.AreNotEqual(0, schema.Count());
            }
            catch (Exception e)
            {
                Assert.Fail("Schema creation fails [{0}]", e.Message);
            }
        }

        [Test]
        public void CanSessionOpensWithConfiguration()
        {
            try
            {
                ISessionFactory s = conf.BuildSessionFactory();
                ISession si = s.OpenSession();

                Assert.IsTrue(s != null);
                Assert.IsTrue(si != null);
                Assert.AreNotEqual(si.Connection.State, ConnectionState.Closed);

                si.Close();
            }
            catch (Exception e)
            {
                Assert.Fail(e.Message);
            }
        }


        [Test, Explicit]
        public void CanCreateSchemaScript()
        {
            const string _sqlSchema = @"../../../../db/Schema.sql";
            var export = new SchemaExport(conf);
            export.SetOutputFile(_sqlSchema);
            export.Execute(true, true, false);
        }

    }
} ;